<!-- build time:Tue Feb 18 2020 17:19:27 GMT+0800 (China Standard Time) --><!DOCTYPE html><html lang="zh"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui"><meta name="renderer" content="webkit"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="format-detection" content="telephone=no,email=no,adress=no"><meta name="theme-color" content="#000000"><meta http-equiv="window-target" content="_top"><title>Spring Boot Dubbo 入门 | calm&#39;s blog</title><meta name="description" content="摘要: 原创出处 http:&#x2F;&#x2F;www.iocoder.cn&#x2F;Spring-Boot&#x2F;Dubbo&#x2F; 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！1. 概述FROM Dubbo 官网Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC 框架，它提供了三大核心能力：面向接口的远程方法调用，智能容错和负载均衡，以及服务自动注册和发现。事实上，大多数"><meta property="og:type" content="article"><meta property="og:title" content="Spring Boot Dubbo 入门"><meta property="og:url" content="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/index.html"><meta property="og:site_name" content="卡姆"><meta property="og:description" content="摘要: 原创出处 http:&#x2F;&#x2F;www.iocoder.cn&#x2F;Spring-Boot&#x2F;Dubbo&#x2F; 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！1. 概述FROM Dubbo 官网Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC 框架，它提供了三大核心能力：面向接口的远程方法调用，智能容错和负载均衡，以及服务自动注册和发现。事实上，大多数"><meta property="og:locale" content="zh_CN"><meta property="og:image" content="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/1.png"><meta property="og:image" content="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/2.png"><meta property="og:image" content="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/3.png"><meta property="og:image" content="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/4.png"><meta property="article:published_time" content="2020-01-07T03:25:17.000Z"><meta property="article:modified_time" content="2020-01-07T06:31:58.684Z"><meta property="article:author" content="calm"><meta property="article:tag" content="dubbo"><meta name="twitter:card" content="summary"><meta name="twitter:image" content="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/1.png"><link rel="canonical" href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/index.html"><link rel="alternate" href="/atom.xml" title="卡姆" type="application/atom+xml"><link rel="icon" href="/blog/images/avatar.jpg" type="image/x-icon"><link rel="stylesheet" href="/blog/css/style.css"><link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.css"><meta name="generator" content="Hexo 4.2.0"></head><body class="main-center theme-green" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="slimContent"><div class="navbar-header"><div class="profile-block text-center"><a id="avatar" href="https://github.com/yuyong725" target="_blank"><img class="img-circle img-rotate" src="/blog/images/avatar.jpg" width="200" height="200"></a><h2 id="name" class="hidden-xs hidden-sm">卡姆</h2><h3 id="title" class="hidden-xs hidden-sm hidden-md">a Java coder need calm</h3><small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i> hangzhou, China</small></div><div class="search" id="search-form-wrap"><form class="search-form sidebar-form"><div class="input-group"><input type="text" class="search-form-input form-control" placeholder="搜索"> <span class="input-group-btn"><button type="submit" class="search-form-submit btn btn-flat" onclick="return!1"><i class="icon icon-search"></i></button></span></div></form><div class="ins-search"><div class="ins-search-mask"></div><div class="ins-search-container"><div class="ins-input-wrapper"><input type="text" class="ins-search-input" placeholder="想要查找什么..." x-webkit-speech> <button type="button" class="close ins-close ins-selectable" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="ins-section-wrapper"><div class="ins-section-container"></div></div></div></div></div><button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button></div><nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation"><ul class="nav navbar-nav main-nav"><li class="menu-item menu-item-home"><a href="/blog/."><i class="icon icon-home-fill"></i> <span class="menu-title">首页</span></a></li><li class="menu-item menu-item-archives"><a href="/blog/archives"><i class="icon icon-archives-fill"></i> <span class="menu-title">归档</span></a></li><li class="menu-item menu-item-categories"><a href="/blog/categories"><i class="icon icon-folder"></i> <span class="menu-title">分类</span></a></li><li class="menu-item menu-item-tags"><a href="/blog/tags"><i class="icon icon-tags"></i> <span class="menu-title">标签</span></a></li><li class="menu-item menu-item-repository"><a href="/blog/repository"><i class="icon icon-project"></i> <span class="menu-title">项目</span></a></li><li class="menu-item menu-item-books"><a href="/blog/books"><i class="icon icon-book-fill"></i> <span class="menu-title">书单</span></a></li><li class="menu-item menu-item-links"><a href="/blog/links"><i class="icon icon-friendship"></i> <span class="menu-title">友链</span></a></li><li class="menu-item menu-item-about"><a href="/blog/about"><i class="icon icon-cup-fill"></i> <span class="menu-title">关于</span></a></li></ul><ul class="social-links"><li><a href="https://github.com/yuyong725" target="_blank" title="Github" data-toggle="tooltip" data-placement="top"><i class="icon icon-github"></i></a></li></ul></nav></div></header><aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar"><div class="slimContent"><div class="widget"><h3 class="widget-title">公告</h3><div class="widget-body"><div id="board"><div class="content"><p>欢迎交流与分享经验!</p></div></div></div></div><div class="widget"><h3 class="widget-title">分类</h3><div class="widget-body"><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/blog/categories/CI-DI/">CI/DI</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/git/">git</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/labs/">labs</a><span class="category-list-count">14</span></li></ul></div></div><div class="widget"><h3 class="widget-title">标签</h3><div class="widget-body"><ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/MyBatis/" rel="tag">MyBatis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Oauth2/" rel="tag">Oauth2</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Spring-Security/" rel="tag">Spring Security</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/SpringMVC/" rel="tag">SpringMVC</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/docker/" rel="tag">docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/dubbo/" rel="tag">dubbo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/git/" rel="tag">git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jdbctemplate/" rel="tag">jdbctemplate</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jerkins/" rel="tag">jerkins</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/job/" rel="tag">job</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jpa/" rel="tag">jpa</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/pdf/" rel="tag">pdf</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/redis/" rel="tag">redis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8/" rel="tag">分库分表</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" rel="tag">多数据源</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/" rel="tag">数据库版本管理</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/" rel="tag">数据库连接池</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E7%BC%93%E5%AD%98/" rel="tag">缓存</a><span class="tag-list-count">2</span></li></ul></div></div><div class="widget"><h3 class="widget-title">标签云</h3><div class="widget-body tagcloud"><a href="/blog/tags/MyBatis/" style="font-size:13px">MyBatis</a> <a href="/blog/tags/Oauth2/" style="font-size:13px">Oauth2</a> <a href="/blog/tags/Spring-Security/" style="font-size:13px">Spring Security</a> <a href="/blog/tags/SpringMVC/" style="font-size:13px">SpringMVC</a> <a href="/blog/tags/docker/" style="font-size:13px">docker</a> <a href="/blog/tags/dubbo/" style="font-size:13px">dubbo</a> <a href="/blog/tags/git/" style="font-size:13px">git</a> <a href="/blog/tags/jdbctemplate/" style="font-size:13px">jdbctemplate</a> <a href="/blog/tags/jerkins/" style="font-size:13px">jerkins</a> <a href="/blog/tags/job/" style="font-size:14px">job</a> <a href="/blog/tags/jpa/" style="font-size:13px">jpa</a> <a href="/blog/tags/pdf/" style="font-size:13px">pdf</a> <a href="/blog/tags/redis/" style="font-size:13px">redis</a> <a href="/blog/tags/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8/" style="font-size:13px">分库分表</a> <a href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" style="font-size:13px">多数据源</a> <a href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/" style="font-size:13px">数据库版本管理</a> <a href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/" style="font-size:13px">数据库连接池</a> <a href="/blog/tags/%E7%BC%93%E5%AD%98/" style="font-size:14px">缓存</a></div></div><div class="widget"><h3 class="widget-title">归档</h3><div class="widget-body"><ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2020/02/">二月 2020</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2020/01/">一月 2020</a><span class="archive-list-count">8</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2019/12/">十二月 2019</a><span class="archive-list-count">4</span></li></ul></div></div><div class="widget"><h3 class="widget-title">最新文章</h3><div class="widget-body"><ul class="recent-post-list list-unstyled no-thumbnail"><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/18/labs-springboot2-async-Job/" class="title">Spring Boot 异步任务入门</a></p><p class="item-date"><time datetime="2020-02-18T05:10:39.000Z" itemprop="datePublished">2020-02-18</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/18/labs-springboot2-job/" class="title">Spring Boot 定时任务入门</a></p><p class="item-date"><time datetime="2020-02-18T02:26:31.000Z" itemprop="datePublished">2020-02-18</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/17/labs-springboot2-database-version-control/" class="title">Spring Boot 数据库版本管理入门</a></p><p class="item-date"><time datetime="2020-02-17T12:05:54.000Z" itemprop="datePublished">2020-02-17</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/17/labs-springboot2-sharding-datasource/" class="title">Spring Boot 分库分表入门</a></p><p class="item-date"><time datetime="2020-02-17T08:15:31.000Z" itemprop="datePublished">2020-02-17</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/13/labs-springboot2-dynamic-datasource/" class="title">Spring Boot 多数据源（读写分离）入门</a></p><p class="item-date"><time datetime="2020-02-13T03:18:37.000Z" itemprop="datePublished">2020-02-13</time></p></div></li></ul></div></div></div></aside><aside class="sidebar sidebar-toc collapse in" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar"><div class="slimContent"><nav id="toc" class="article-toc"><h3 class="toc-title">文章目录</h3><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-概述"><span class="toc-text">1. 概述</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-XML-配置"><span class="toc-text">2. XML 配置</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-API"><span class="toc-text">2.1 API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-1-UserDTO"><span class="toc-text">2.1.1 UserDTO</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-2-UserRpcService"><span class="toc-text">2.1.2 UserRpcService</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-Provider"><span class="toc-text">2.2 Provider</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-1-引入依赖"><span class="toc-text">2.2.1 引入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-2-应用配置文件"><span class="toc-text">2.2.2 应用配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-3-UserRpcServiceImpl"><span class="toc-text">2.2.3 UserRpcServiceImpl</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-4-Dubbo-XML-配置文件"><span class="toc-text">2.2.4 Dubbo XML 配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-5-ProviderApplication"><span class="toc-text">2.2.5 ProviderApplication</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-Consumer"><span class="toc-text">2.3 Consumer</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-1-引入依赖"><span class="toc-text">2.3.1 引入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-2-应用配置文件"><span class="toc-text">2.3.2 应用配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-3-Dubbo-XML-配置文件"><span class="toc-text">2.3.3 Dubbo XML 配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-4-ConsumerApplication"><span class="toc-text">2.3.4 ConsumerApplication</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-注解配置"><span class="toc-text">3. 注解配置</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-API"><span class="toc-text">3.1 API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-1-UserDTO"><span class="toc-text">3.1.1 UserDTO</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-2-UserRpcService"><span class="toc-text">3.1.2 UserRpcService</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-2-Provider"><span class="toc-text">3.2 Provider</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-1-引入依赖"><span class="toc-text">3.2.1 引入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-2-应用配置文件"><span class="toc-text">3.2.2 应用配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-3-UserRpcServiceImpl"><span class="toc-text">3.2.3 UserRpcServiceImpl</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-4-ProviderApplication"><span class="toc-text">3.2.4 ProviderApplication</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-3-Consumer"><span class="toc-text">3.3 Consumer</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-1-引入依赖"><span class="toc-text">3.3.1 引入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-2-应用配置文件"><span class="toc-text">3.3.2 应用配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-3-ConsumerApplication"><span class="toc-text">3.3.3 ConsumerApplication</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-4-选择注解还是-XML-配置？"><span class="toc-text">3.4 选择注解还是 XML 配置？</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#4-参数验证"><span class="toc-text">4. 参数验证</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-API"><span class="toc-text">4.1 API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-1-引入依赖"><span class="toc-text">4.1.1 引入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-2-UserAddDTO"><span class="toc-text">4.1.2 UserAddDTO</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-3-UserRpcService"><span class="toc-text">4.1.3 UserRpcService</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-2-Provider"><span class="toc-text">4.2 Provider</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-1-UserRpcServiceImpl"><span class="toc-text">4.2.1 UserRpcServiceImpl</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-2-Dubbo-XML-配置文件"><span class="toc-text">4.2.2 Dubbo XML 配置文件</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-3-Consumer"><span class="toc-text">4.3 Consumer</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-1-Dubbo-XML-配置文件"><span class="toc-text">4.3.1 Dubbo XML 配置文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-2-ConsumerApplication"><span class="toc-text">4.3.2 ConsumerApplication</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-4-存在的问题"><span class="toc-text">4.4 存在的问题</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#5-自定义实现拓展点"><span class="toc-text">5. 自定义实现拓展点</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#5-1-API"><span class="toc-text">5.1 API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-1-ServiceExceptionEnum"><span class="toc-text">5.1.1 ServiceExceptionEnum</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-2-ServiceException"><span class="toc-text">5.1.2 ServiceException</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-2-Provider"><span class="toc-text">5.2 Provider</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-1-DubboExceptionFilter"><span class="toc-text">5.2.1 DubboExceptionFilter</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-2-UserRpcServiceImpl"><span class="toc-text">5.2.2 UserRpcServiceImpl</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-3-添加SPI文件"><span class="toc-text">5.2.3 添加SPI文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-4-应用配置文件"><span class="toc-text">5.2.4 应用配置文件</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-3-Consumer"><span class="toc-text">5.3 Consumer</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-3-1-ConsumerApplication"><span class="toc-text">5.3.1 ConsumerApplication</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-4-小结"><span class="toc-text">5.4 小结</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#666-彩蛋"><span class="toc-text">666. 彩蛋</span></a></li></ol></nav></div></aside><main class="main" role="main"><div class="content"><article id="post-labs-springboot2-dubbo" class="article article-type-post" itemscope itemtype="http://schema.org/BlogPosting"><div class="article-header"><h1 class="article-title" itemprop="name">Spring Boot Dubbo 入门</h1><div class="article-meta"><span class="article-date"><i class="icon icon-calendar-check"></i> <a href="/blog/2020/01/07/labs-springboot2-dubbo/" class="article-date"><time datetime="2020-01-07T03:25:17.000Z" itemprop="datePublished">2020-01-07</time></a></span> <span class="article-category"><i class="icon icon-folder"></i> <a class="article-category-link" href="/blog/categories/labs/">labs</a></span> <span class="article-tag"><i class="icon icon-tags"></i> <a class="article-tag-link" href="/blog/tags/dubbo/" rel="tag">dubbo</a></span> <span class="article-read hidden-xs"><i class="icon icon-eye-fill" aria-hidden="true"></i> <span id="busuanzi_container_page_pv"><span id="busuanzi_value_page_pv">0</span></span></span> <span class="post-comment"><i class="icon icon-comment"></i> <a href="/blog/2020/01/07/labs-springboot2-dubbo/#comments" class="article-comment-link">评论</a></span> <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计: 12.2k(字)</span> <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长: 55(分)</span></div></div><div class="article-entry marked-body" itemprop="articleBody"><blockquote><p>摘要: 原创出处 <a href="http://www.iocoder.cn/Spring-Boot/Dubbo/" target="_blank" rel="noopener">http://www.iocoder.cn/Spring-Boot/Dubbo/</a> 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！</p></blockquote><h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h1><blockquote><p>FROM <a href="http://dubbo.apache.org/zh-cn/" target="_blank" rel="noopener">Dubbo 官网</a></p><p>Apache Dubbo <strong>|ˈdʌbəʊ|</strong> 是一款高性能、轻量级的开源 Java RPC 框架，它提供了三大核心能力：面向接口的远程方法调用，智能容错和负载均衡，以及服务自动注册和发现。</p><p>事实上，大多数<strong>成熟的</strong>开源项目，都是 KPI 驱动，又或者背后有商业化支撑</p></blockquote><p><img src="1.png" alt="Dubbo 整体架构"></p><p>图中，一共涉及到 5 个角色：</p><ul><li>Registry 注册中心，用于服务的注册与发现。</li><li>Provider 服务提供者，通过向 Registry 注册服务。</li><li>Consumer 服务消费者，通过从 Registry 发现服务。后续直接调用 Provider ，无需经过 Registry 。</li><li>Monitor 监控中心，统计服务的调用次数和调用时间。</li><li>Container 服务运行容器。</li></ul><blockquote><p>FROM <a href="http://dubbo.apache.org/zh-cn/docs/user/preface/architecture.html" target="_blank" rel="noopener">《Dubbo 文档 —— 架构》</a></p><p>调用关系说明（注意，和上图的数字，和下面的步骤是一一对应的）：</p><ul><li><ol><li>服务容器负责启动，加载，运行服务提供者。</li></ol></li><li><ol><li>服务提供者在启动时，向注册中心注册自己提供的服务。</li></ol></li><li><ol><li>服务消费者在启动时，向注册中心订阅自己所需的服务。</li></ol></li><li><ol><li>注册中心返回服务提供者地址列表给消费者，如果有变更，注册中心将基于长连接推送变更数据给消费者。</li></ol></li><li><ol><li>服务消费者，从提供者地址列表中，基于软负载均衡算法，选一台提供者进行调用，如果调用失败，再选另一台调用。</li></ol></li><li><ol><li>服务消费者和提供者，在内存中累计调用次数和调用时间，定时每分钟发送一次统计数据到监控中心。</li></ol></li></ul></blockquote><p>本文的重心，在于一起入门 Provider 和 Consumer 的代码编写，这也是实际项目开发中，我们涉及到的角色。</p><p>Dubbo 提供了比较多的<a href="http://dubbo.apache.org/zh-cn/docs/user/configuration/xml.html" target="_blank" rel="noopener">配置</a>方式，日常开发中主要使用的是 <a href="http://dubbo.apache.org/zh-cn/docs/user/configuration/xml.html" target="_blank" rel="noopener">XML 配置</a> 和 <a href="http://dubbo.apache.org/zh-cn/docs/user/configuration/annotation.html" target="_blank" rel="noopener">注解配置</a> 。我们分别会在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2. XML 配合」</a> 和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「3. 注解配置」</a> 小节来入门。</p><p>考虑到现在 Dubbo 已经提供了 <a href="https://github.com/apache/dubbo-spring-boot-project" target="_blank" rel="noopener">dubbo-spring-boot-project</a> 项目，集成到 Spring Boot 体系中，而大家都基本采用 Spring Boot 框架，所以我们就不像 <a href="http://dubbo.apache.org/zh-cn/docs/user/quick-start.html" target="_blank" rel="noopener">Dubbo 官方文档</a> 一样，提供的是 Spring 环境下的示例，而<strong>是 Spring Boot 环境下</strong>。</p><h1 id="2-XML-配置"><a href="#2-XML-配置" class="headerlink" title="2. XML 配置"></a>2. XML 配置</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-rpc/labs-springboot2-dubbo/labs-springboot2-dubbo-xml" target="_blank" rel="noopener">labs-springboot2-dubbo-xml</a> 。</p></blockquote><p>本小节的示例，需要创建三个 Maven 项目，如下图所示：</p><p><img src="2.png" alt="三个 Maven 项目"></p><ul><li><code>labs-springboot2-dubbo-xml-api</code> 项目：<strong>服务接口</strong>，定义 Dubbo Service API 接口，提供给消费者使用。详细代码，我们在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.1 API」</a> 讲解。</li><li><code>labs-springboot2-dubbo-xml-provider</code> 项目：<strong>服务提供者</strong>，实现 <code>user-rpc-service-api</code> 项目定义的 Dubbo Service API 接口，提供相应的服务。详细代码，我们在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2 Provider」</a> 中讲解。</li><li><code>labs-springboot2-dubbo-xml-consumer</code> 项目：<strong>服务消费者</strong>，会调用 <code>user-rpc-service-provider</code> 项目提供的 Dubbo Service 服务。详细代码，我们在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.3 Consumer」</a> 中讲解。</li></ul><h2 id="2-1-API"><a href="#2-1-API" class="headerlink" title="2.1 API"></a>2.1 API</h2><p>对应 <code>labs-springboot2-dubbo-xml-api</code> 项目，<strong>服务接口</strong>，定义 Dubbo Service API 接口，提供给消费者使用。</p><h3 id="2-1-1-UserDTO"><a href="#2-1-1-UserDTO" class="headerlink" title="2.1.1 UserDTO"></a>2.1.1 UserDTO</h3><p>创建用于 Dubbo Service 传输类。这里，我们创建 <code>UserDTO</code> 类，用户信息 DTO 。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserDTO.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors</span>(chain = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserDTO</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用户编号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 昵称</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 性别</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Integer gender;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意，要实现 <a href="https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/io/Serializable.java" target="_blank" rel="noopener">java.io.Serializable</a> 接口。因为，Dubbo RPC 会涉及远程通信，需要序列化和反序列化。</p><h3 id="2-1-2-UserRpcService"><a href="#2-1-2-UserRpcService" class="headerlink" title="2.1.2 UserRpcService"></a>2.1.2 UserRpcService</h3><p>创建 Dubbo Service API 接口。这里，我们创建 <code>UserRpcService</code> 接口，用户服务 RPC Service 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcService.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRpcService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据指定用户编号，获得用户信息</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 用户编号</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 用户信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">UserDTO <span class="title">get</span><span class="params">(Integer id)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-2-Provider"><a href="#2-2-Provider" class="headerlink" title="2.2 Provider"></a>2.2 Provider</h2><p>对应 <code>labs-springboot2-dubbo-xml-provider</code> 项目，<strong>服务提供者</strong>，实现 <code>labs-springboot2-dubbo-xml-api</code> 项目定义的 Dubbo Service API 接口，提供相应的服务。</p><h3 id="2-2-1-引入依赖"><a href="#2-2-1-引入依赖" class="headerlink" title="2.2.1 引入依赖"></a>2.2.1 引入依赖</h3><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml-provider<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 引入定义的 Dubbo API 接口 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 引入 Spring Boot 依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 Dubbo 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 使用 Zookeeper 作为注册中心 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-framework<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-recipes<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li><p>因为我们希望实现对 Dubbo 的自动化配置，所以引入 <a href="https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter" target="_blank" rel="noopener">dubbo-spring-boot-starter</a> 依赖。</p><blockquote><p>FROM <a href="https://www.oschina.net/p/curator" target="_blank" rel="noopener">https://www.oschina.net/p/curator</a></p><p>Zookeeper 的客户端调用过于复杂，Apache Curator 就是为了简化Zookeeper 客户端调用而生，利用它，可以更好的使用 Zookeeper。</p><ul><li>虽然说，目前阿里正在大力推广 <a href="https://github.com/alibaba/nacos" target="_blank" rel="noopener">Nacos</a> 作为 Dubbo 的注册中心，但是大多数团队，采用的还是 Zookeeper 为主。</li><li>对了，如果胖友不知道怎么安装 Zookeeper ，可以看看 <a href="http://www.iocoder.cn/Zookeeper/install/?self" target="_blank" rel="noopener">《芋道 Zookeeper 安装部署》</a> 文章。</li></ul></blockquote></li></ul><h3 id="2-2-2-应用配置文件"><a href="#2-2-2-应用配置文件" class="headerlink" title="2.2.2 应用配置文件"></a>2.2.2 应用配置文件</h3><p>在 <code>resources</code> 目录下， 创建 <code>application.yml</code>配置文件，添加 Dubbo 相关的配置，如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># dubbo 配置项，对应 DubboConfigurationProperties 配置类</span></span><br><span class="line"><span class="attr">dubbo:</span></span><br><span class="line">  <span class="comment"># Dubbo 应用配置</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">user-service-provider</span> <span class="comment"># 应用名</span></span><br><span class="line">  <span class="comment"># Dubbo 注册中心配</span></span><br><span class="line">  <span class="attr">registry:</span></span><br><span class="line">    <span class="attr">address:</span> <span class="string">zookeeper://127.0.0.1:2181</span> <span class="comment"># 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。</span></span><br><span class="line">  <span class="comment"># Dubbo 服务提供者协议配置</span></span><br><span class="line">  <span class="attr">protocol:</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">-1</span> <span class="comment"># 协议端口。使用 -1 表示随机端口。</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">dubbo</span> <span class="comment"># 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档</span></span><br><span class="line">  <span class="comment"># Dubbo 服务提供者配置</span></span><br><span class="line">  <span class="attr">provider:</span></span><br><span class="line">    <span class="attr">timeout:</span> <span class="number">1000</span> <span class="comment"># 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改</span></span><br><span class="line">    <span class="attr">UserRpcService:</span></span><br><span class="line">      <span class="attr">version:</span> <span class="number">1.0</span><span class="number">.0</span></span><br></pre></td></tr></table></figure><ul><li><blockquote><p>卡姆：本小节，我们的 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「XML 配置」</a> ，指的是使用 XML 来配置 Dubbo Service 服务。如果胖友想看纯粹的全量 XML 配置，可以看看 <a href="http://dubbo.apache.org/zh-cn/docs/user/configuration/xml.html" target="_blank" rel="noopener">《Dubbo 官方文档 —— XML 配置》</a> 。</p></blockquote></li><li><p><code>dubbo.application</code> 配置项，Dubbo 应用信息配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java" target="_blank" rel="noopener">ApplicationConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-application.html" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:application》</a> 。</p></li><li><p><code>dubbo.registry</code> 配置项，Dubbo 注册中心配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java" target="_blank" rel="noopener">RegistryConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-registry.html" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:registry》</a> 。</p></li><li><p><code>dubbo.protocol</code> 配置项，Dubbo 服务提供者协议配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java" target="_blank" rel="noopener">ProtocolConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:protocol》</a> 。</p></li><li><p><code>dubbo.provider</code> 配置项，Dubbo 服务提供者配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java" target="_blank" rel="noopener">ProviderConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-provider.html" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:provider》</a> 。</p></li><li><p><code>dubbo.provider.UserRpcService</code> 配置项，<strong>是我们自定义的</strong>，设置每个 Service 服务的配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfig.java" target="_blank" rel="noopener">ServiceConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:service》</a> 。</p></li></ul><h3 id="2-2-3-UserRpcServiceImpl"><a href="#2-2-3-UserRpcServiceImpl" class="headerlink" title="2.2.3 UserRpcServiceImpl"></a>2.2.3 UserRpcServiceImpl</h3><p>创建 Dubbo Service 实现类。这里，我们创建 <code>UserRpcServiceImpl</code> 类，用户服务 RPC Service 实现类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcServiceImpl.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserRpcServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserRpcService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> UserDTO <span class="title">get</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> UserDTO().setId(id)</span><br><span class="line">                .setName(<span class="string">"没有昵称："</span> + id)</span><br><span class="line">                <span class="comment">// 1 - 男；2 - 女</span></span><br><span class="line">                .setGender(id % <span class="number">2</span> + <span class="number">1</span>); </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>实现 UserRpcService 接口，提供 UserRpcService Dubbo 服务。</li><li>注意，在类上添加了 <strong>Spring</strong> <code>@Service</code> 注解，暴露出 UserRpcServiceImpl Bean 对象。 后续，我们会将该 Bean 暴露成 UserRpcService Dubbo 服务，注册其到注册中心中，并提供相应的 Dubbo 服务。</li></ul><h3 id="2-2-4-Dubbo-XML-配置文件"><a href="#2-2-4-Dubbo-XML-配置文件" class="headerlink" title="2.2.4 Dubbo XML 配置文件"></a>2.2.4 Dubbo XML 配置文件</h3><p>在 <code>resources</code>目录下， 创建 <code>dubbo.xml</code>配置文件，添加 Dubbo 的 Service 服务提供者，如下：</p><blockquote><p>注意⚠️：不要在这里配置<code>&lt;dubbo:provider /&gt;</code> ，这会和👆的yaml冲突，导致启动报错</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version&#x3D;&quot;1.0&quot; encoding&#x3D;&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;beans xmlns:xsi&#x3D;&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2001&#x2F;XMLSchema-instance&quot;</span><br><span class="line">       xmlns:dubbo&#x3D;&quot;http:&#x2F;&#x2F;dubbo.apache.org&#x2F;schema&#x2F;dubbo&quot;</span><br><span class="line">       xmlns&#x3D;&quot;http:&#x2F;&#x2F;www.springframework.org&#x2F;schema&#x2F;beans&quot;</span><br><span class="line">       xsi:schemaLocation&#x3D;&quot;http:&#x2F;&#x2F;www.springframework.org&#x2F;schema&#x2F;beans</span><br><span class="line">       http:&#x2F;&#x2F;www.springframework.org&#x2F;schema&#x2F;beans&#x2F;spring-beans.xsd</span><br><span class="line">       http:&#x2F;&#x2F;dubbo.apache.org&#x2F;schema&#x2F;dubbo</span><br><span class="line">       http:&#x2F;&#x2F;dubbo.apache.org&#x2F;schema&#x2F;dubbo&#x2F;dubbo.xsd&quot;&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- 服务提供者暴露服务配置 --&gt;</span><br><span class="line">    &lt;dubbo:service ref&#x3D;&quot;userRpcServiceImpl&quot; interface&#x3D;&quot;cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService&quot;</span><br><span class="line">        version&#x3D;&quot;$&#123;dubbo.provider.UserRpcService.version&#125;&quot; &#x2F;&gt;</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;beans&gt;</span><br></pre></td></tr></table></figure><ul><li>使用 Dubbo 自定义的 Spring <code>&lt;dubbo:service&gt;</code> 标签，配置我们 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.3 UserRpcServiceImpl」</a> 成 UserRpcService 的 Dubbo 服务提供者。</li></ul><p>更多 <code>&lt;dubbo:service&gt;</code> 标签的属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:service》</a> 。</p><h3 id="2-2-5-ProviderApplication"><a href="#2-2-5-ProviderApplication" class="headerlink" title="2.2.5 ProviderApplication"></a>2.2.5 ProviderApplication</h3><p>创建 <code>ProviderApplication</code> 类，用于启动该项目，提供 Dubbo 服务。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ProviderApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ImportResource</span>(<span class="string">"classpath:dubbo.xml"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProviderApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 启动 Spring Boot 应用</span></span><br><span class="line">        SpringApplication.run(ProviderApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在类上，添加 <code>@ImportResource</code> 注解，引入 <code>dubbo.xml</code> 配置文件。</li></ul><p>运行 <code>#main(String[] args)</code> 方法，启动项目。控制台打印日志如下：</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ... 省略其它日志</span></span><br><span class="line"></span><br><span class="line"><span class="number">2020</span>-<span class="number">01</span>-<span class="number">07</span> <span class="number">11</span>:<span class="number">52</span>:<span class="number">05</span><span class="variable">.847</span>  INFO <span class="number">12049</span> --- [pool-<span class="number">1</span>-thread-<span class="number">1</span>] <span class="variable">.b</span><span class="variable">.c</span><span class="variable">.e</span><span class="variable">.AwaitingNonWebApplicationListener</span> :  [Dubbo] Current Spring Boot Application is await...</span><br></pre></td></tr></table></figure><ul><li>看到该日志内容，意味着启动成功。</li></ul><p>我们来使用 Zookeeper 客户端，查看 UserRpcService 服务是否注册成功。操作流程如下：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 Zookeeper 自带的客户端，连接到 Zookeeper 服务器</span></span><br><span class="line">$ bin/zkCli.sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 /dubbo 目录下的所有服务。</span></span><br><span class="line"><span class="comment"># 此时，我们查看到了 UserRpcService 服务</span></span><br><span class="line">$ ls /dubbo</span><br><span class="line">[cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 /dubbo/cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService 目录下的存储情况。</span></span><br><span class="line"><span class="comment"># 此时，我们看到了 consumers 消费者信息，providers 提供者信息，routers 路由信息，configurators 配置信息。第一次启动消费时，只会看到 configurators 和 providers，这是没有问题的</span></span><br><span class="line">$ ls /dubbo/cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService</span><br><span class="line">[consumers, configurators, routers, providers]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 UserRpcService 服务的节点列表</span></span><br><span class="line"><span class="comment"># 此时，可以看到有一个节点，就是我们刚启动的服务提供者。</span></span><br><span class="line">$ ls /dubbo/cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService/providers</span><br><span class="line">[dubbo%3A%2F%2F192.168.31.225%3A20880%2Fcn.javadog.labs.springboot.dubbo.xml.api.UserRpcService%3Fanyhost%3Dtrue%26application%3Duser-service-provider%26bean.name%3Dcn.javadog.labs.springboot.dubbo.xml.api.UserRpcService%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dcn.javadog.labs.springboot.dubbo.xml.api.UserRpcService%26methods%3Dadd%2Cget%26pid%3D12049%26release%3D2.7.4.1%26revision%3D1.0.0%26service.filter%3D-exception%2CcalmDubboExceptionFilter%26side%3Dprovider%26timeout%3D1000%26timestamp%3D1578369125402%26version%3D1.0.0]</span><br></pre></td></tr></table></figure><p>想要了解更多 Dubbo 是如何使用 Zookeeper 存储数据的，可以看看 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/registry/zookeeper.html" target="_blank" rel="noopener">《Dubbo 文档 —— Zookeeper 注册中心》</a> 文档。</p><h2 id="2-3-Consumer"><a href="#2-3-Consumer" class="headerlink" title="2.3 Consumer"></a>2.3 Consumer</h2><p>对应 <code>labs-springboot2-dubbo-xml-consumer</code> 项目，<strong>服务消费者</strong>，会调用 <code>labs-springboot2-dubbo-xml-provider</code> 项目提供的 Dubbo Service 服务。</p><h3 id="2-3-1-引入依赖"><a href="#2-3-1-引入依赖" class="headerlink" title="2.3.1 引入依赖"></a>2.3.1 引入依赖</h3><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml-consumer<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 引入定义的 Dubbo API 接口 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 引入 Spring Boot 依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 Dubbo 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 使用 Zookeeper 作为注册中心 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-framework<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-recipes<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.1 引入依赖」</a> 一模一样，除了 <code>&lt;artifactId /&gt;</code> 改成了 <code>labs-springboot2-dubbo-xml-consumer</code> 值。</li></ul><h3 id="2-3-2-应用配置文件"><a href="#2-3-2-应用配置文件" class="headerlink" title="2.3.2 应用配置文件"></a>2.3.2 应用配置文件</h3><p>在 <code>resources</code> 目录下， 创建 application.yml` 配置文件，添加 Dubbo 相关的配置，如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"># dubbo 配置项，对应 DubboConfigurationProperties 配置类</span><br><span class="line">dubbo:</span><br><span class="line">  # Dubbo 应用配置</span><br><span class="line">  application:</span><br><span class="line">    name: user-service-consumer # 应用名</span><br><span class="line">  # Dubbo 注册中心配置</span><br><span class="line">  registry:</span><br><span class="line">    address: zookeeper:&#x2F;&#x2F;127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http:&#x2F;&#x2F;dubbo.apache.org&#x2F;zh-cn&#x2F;docs&#x2F;user&#x2F;references&#x2F;registry&#x2F;introduction.html 文档。</span><br><span class="line">  # Dubbo 消费者配置</span><br><span class="line">  consumer:</span><br><span class="line">    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改</span><br><span class="line">    UserRpcService:</span><br><span class="line">      version: 1.0.0</span><br></pre></td></tr></table></figure><ul><li>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.2 应用配置文件」</a> 看起来有点类似，我们仅仅说说差异性。</li><li>去掉 <code>dubbo.protocol</code> 配置项，因为我们是作为 Dubbo 服务的消费者，所以无需添加 Dubbo 服务提供者协议配置。</li><li><code>dubbo.consumer</code> 配置项，Dubbo 服务消费者配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java" target="_blank" rel="noopener">ConsumerConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:consumer》</a> 。</li><li><code>dubbo.consumer.UserRpcService</code> 配置项，<strong>是我们自定义的</strong>，设置每个 Service 服务的配置。更多属性，可见 <a href="https://github.com/apache/dubbo/blob/3257ef48f524c748c0651e97f6a8d06ff2963c94/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfig.java" target="_blank" rel="noopener">ReferenceConfig</a> 类。每个属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-reference" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:reference》</a> 。</li></ul><h3 id="2-3-3-Dubbo-XML-配置文件"><a href="#2-3-3-Dubbo-XML-配置文件" class="headerlink" title="2.3.3 Dubbo XML 配置文件"></a>2.3.3 Dubbo XML 配置文件</h3><p>在 <code>resources</code> 目录下，创建 <code>dubbo.xml</code> 配置文件，添加 Dubbo 的 Service 服务引用者，如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:dubbo</span>=<span class="string">"http://dubbo.apache.org/schema/dubbo"</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns</span>=<span class="string">"http://www.springframework.org/schema/beans"</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="tag"><span class="string">       http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">       http://dubbo.apache.org/schema/dubbo</span></span></span><br><span class="line"><span class="tag"><span class="string">       http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 服务消费者引用服务配置 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dubbo:reference</span> <span class="attr">id</span>=<span class="string">"userService"</span> <span class="attr">interface</span>=<span class="string">"cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService"</span></span></span><br><span class="line"><span class="tag">                     <span class="attr">version</span>=<span class="string">"$&#123;dubbo.consumer.UserRpcService.version&#125;"</span>/&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>使用 Dubbo 自定义的 Spring <code>&lt;dubbo:reference&gt;</code> 标签，引用 UserRpcService 接口对应的 Dubbo Service 服务，并创建一个 Bean 编号为 <code>userService</code> 的 Bean 对象。这样，我们在 Spring 中，就可以直接注入 UserRpcService Bean ，后续就可以像一个“<strong>本地</strong>”的 UserRpcService 进行调用使用。</li></ul><p>更多 <code>&lt;dubbo:reference&gt;</code> 标签的属性的说明，可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-reference" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:reference》</a> 。</p><h3 id="2-3-4-ConsumerApplication"><a href="#2-3-4-ConsumerApplication" class="headerlink" title="2.3.4 ConsumerApplication"></a>2.3.4 ConsumerApplication</h3><p>创建 <code>ConsumerApplication</code> 类，用于启动该项目，调用 Dubbo 服务。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ConsumerApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ImportResource</span>(<span class="string">"classpath:dubbo.xml"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConsumerApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 启动 Spring Boot 应用</span></span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Component</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserRpcServiceTest</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(getClass());</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Resource</span></span><br><span class="line">        <span class="keyword">private</span> UserRpcService userRpcService;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            UserDTO user = userRpcService.get(<span class="number">1</span>);</span><br><span class="line">            logger.info(<span class="string">"[run][发起一次 Dubbo RPC 请求，获得用户为(&#123;&#125;)"</span>, user);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在类上，添加 <code>@ImportResource</code> 注解，引入 <code>dubbo.xml</code> 配置文件。</li><li>在 UserRpcServiceTest 中，我们使用 <code>@Resource</code> 注解，引用通过 <code>&lt;dubbo:reference /&gt;</code> 配置的引用的 UserRpcService 服务对应的 UserRpcService Bean 。</li></ul><p>运行 <code>#main(String[] args)</code> 方法，启动项目。控制台打印日志如下：</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">2020</span>-<span class="number">01</span>-<span class="number">07</span> <span class="number">12</span>:<span class="number">06</span>:<span class="number">42</span><span class="variable">.435</span>  INFO <span class="number">12117</span> --- [           main] c<span class="variable">.ConsumerApplication</span>$UserRpcServiceTest : [run][发起一次 Dubbo RPC 请求，获得用户为(UserDTO(id=<span class="number">1</span>, name=没有昵称：<span class="number">1</span>, gender=<span class="number">2</span>))]</span><br></pre></td></tr></table></figure><ul><li>我们在应用启动完成后，成功的发起了一次 UserRpcService 的 Dubbo RPC 的调用。</li></ul><p>我们来使用 Zookeeper 客户端，查看 UserRpcService 服务是否多了一个消费者。操作流程如下：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 Zookeeper 自带的客户端，连接到 Zookeeper 服务器</span></span><br><span class="line">$ bin/zkCli.sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 UserRpcService 服务的消费者列表</span></span><br><span class="line"><span class="comment"># 此时，可以看到有一个节点，就是我们刚启动的服务消费者。</span></span><br><span class="line">$ ls /dubbo/cn.iocoder.springboot.lab30.rpc.api.UserRpcService/consumers/dubbo/cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService/consumers</span><br><span class="line">[consumer%3A%2F%2F192.168.31.225%2Fcn.javadog.labs.springboot.dubbo.xml.api.UserRpcService%3Fapplication%3Duser-service-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dcn.javadog.labs.springboot.dubbo.xml.api.UserRpcService%26lazy%3Dfalse%26methods%3Dadd%2Cget%26pid%3D12117%26qos.enable%3Dfalse%26release%3D2.7.4.1%26revision%3D1.0.0%26side%3Dconsumer%26sticky%3Dfalse%26timeout%3D1000%26timestamp%3D1578370001429%26validation%3Dtrue%26version%3D1.0.0]</span><br></pre></td></tr></table></figure><p>至此，我们已经完成了使用 XML 配置的方式，在 Spring Boot 中使用 Dubbo 的入门。 虽然篇幅长了一点点，但是还是比较简单的。个人建议的话，此时此刻仅仅是看到这里，但是并没有手敲代码的胖友，可以赶紧打开 IDEA 自己敲（“抄”）一波，嘿嘿。</p><h1 id="3-注解配置"><a href="#3-注解配置" class="headerlink" title="3. 注解配置"></a>3. 注解配置</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-rpc/labs-springboot2-dubbo/labs-springboot2-dubbo-annotation" target="_blank" rel="noopener">labs-springboot2-dubbo-annotation</a> 。</p></blockquote><p>本小节的示例，需要创建三个 Maven 项目，如下图所示：<img src="3.png" alt="三个 Maven 项目"></p><ul><li><code>labs-springboot2-dubbo-annotation-api</code> 项目：<strong>服务接口</strong>，定义 Dubbo Service API 接口，提供给消费者使用。详细代码，我们在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「3.1 API」</a> 讲解。</li><li><code>labs-springboot2-dubbo-annotation-provider</code> 项目：<strong>服务提供者</strong>，实现 <code>user-rpc-service-api-02</code> 项目定义的 Dubbo Service API 接口，提供相应的服务。详细代码，我们在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「3.2 Provider」</a> 中讲解。</li><li><code>labs-springboot2-dubbo-annotation-consumer</code> 项目：<strong>服务消费者</strong>，会调用 <code>user-rpc-service-provider-02</code> 项目提供的 Dubbo Service 服务。详细代码，我们在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「3.3 Consumer」</a> 中讲解。</li></ul><h2 id="3-1-API"><a href="#3-1-API" class="headerlink" title="3.1 API"></a>3.1 API</h2><p>对应 <code>labs-springboot2-dubbo-annotation-api</code> 项目，<strong>服务接口</strong>，定义 Dubbo Service API 接口，提供给消费者使用。</p><h3 id="3-1-1-UserDTO"><a href="#3-1-1-UserDTO" class="headerlink" title="3.1.1 UserDTO"></a>3.1.1 UserDTO</h3><blockquote><p>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.1.1 UserDTO」</a> 一致。</p></blockquote><p>创建用于 Dubbo Service 传输类。这里，我们创建 <code>UserDTO</code> 类，用户信息。代码如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; UserDTO.java</span><br><span class="line"></span><br><span class="line">@Data</span><br><span class="line">@Accessors(chain &#x3D; true)</span><br><span class="line">public class UserDTO implements Serializable &#123;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 用户编号</span><br><span class="line">     *&#x2F;</span><br><span class="line">    private Integer id;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 昵称</span><br><span class="line">     *&#x2F;</span><br><span class="line">    private String name;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 性别</span><br><span class="line">     *&#x2F;</span><br><span class="line">    private Integer gender;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意，要实现 <a href="https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/io/Serializable.java" target="_blank" rel="noopener">java.io.Serializable</a> 接口。因为，Dubbo RPC 会涉及远程通信，需要序列化和反序列化。</p><h3 id="3-1-2-UserRpcService"><a href="#3-1-2-UserRpcService" class="headerlink" title="3.1.2 UserRpcService"></a>3.1.2 UserRpcService</h3><blockquote><p>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">3.1.2 UserRpcService」</a> 一致。</p></blockquote><p>创建 Dubbo Service API 接口。这里，我们创建 <code>UserRpcService</code> 接口，用户服务 RPC Service 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcService.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRpcService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据指定用户编号，获得用户信息</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 用户编号</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 用户信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">UserDTO <span class="title">get</span><span class="params">(Integer id)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="3-2-Provider"><a href="#3-2-Provider" class="headerlink" title="3.2 Provider"></a>3.2 Provider</h2><p>对应 <code>labs-springboot2-dubbo-annotation-provider</code> 项目，<strong>服务提供者</strong>，实现 <code>labs-springboot2-dubbo-annotation-api</code> 项目定义的 Dubbo Service API 接口，提供相应的服务。</p><h3 id="3-2-1-引入依赖"><a href="#3-2-1-引入依赖" class="headerlink" title="3.2.1 引入依赖"></a>3.2.1 引入依赖</h3><blockquote><p>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.1 引入依赖」</a> 一致。</p></blockquote><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-annotation<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-annotation-provider<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 引入定义的 Dubbo API 接口 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-annotation-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 引入 Spring Boot 依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 Dubbo 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 使用 Zookeeper 作为注册中心 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-framework<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-recipes<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-2-2-应用配置文件"><a href="#3-2-2-应用配置文件" class="headerlink" title="3.2.2 应用配置文件"></a>3.2.2 应用配置文件</h3><p>在 <code>resources</code> 目录下， 创建 <code>application.yml</code>配置文件，添加 Dubbo 相关的配置，如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"># dubbo 配置项，对应 DubboConfigurationProperties 配置类</span><br><span class="line">dubbo:</span><br><span class="line">  # Dubbo 应用配置</span><br><span class="line">  application:</span><br><span class="line">    name: user-service-provider # 应用名</span><br><span class="line">  # Dubbo 注册中心配</span><br><span class="line">  registry:</span><br><span class="line">    address: zookeeper:&#x2F;&#x2F;127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http:&#x2F;&#x2F;dubbo.apache.org&#x2F;zh-cn&#x2F;docs&#x2F;user&#x2F;references&#x2F;registry&#x2F;introduction.html 文档。</span><br><span class="line">  # Dubbo 服务提供者协议配置</span><br><span class="line">  protocol:</span><br><span class="line">    port: -1 # 协议端口。使用 -1 表示随机端口。</span><br><span class="line">    name: dubbo # 使用 &#96;dubbo:&#x2F;&#x2F;&#96; 协议。更多协议，可见 http:&#x2F;&#x2F;dubbo.apache.org&#x2F;zh-cn&#x2F;docs&#x2F;user&#x2F;references&#x2F;protocol&#x2F;introduction.html 文档</span><br><span class="line">  # Dubbo 服务提供者配置</span><br><span class="line">  provider:</span><br><span class="line">    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改</span><br><span class="line">    UserRpcService:</span><br><span class="line">      version: 1.0.</span><br><span class="line">  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者</span><br><span class="line">  scan:</span><br><span class="line">    base-packages: cn.javadog.labs.springboot.dubbo.annotation.service</span><br></pre></td></tr></table></figure><p>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.2 应用配置」</a> 基本一致，差异在于多出了 <code>dubbo.scan.base-packages</code> 配置项，配置扫描的基础路径，后续会根据该路径，扫描使用了 Dubbo 自定义的 <a href="https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/com/alibaba/dubbo/config/annotation/Service.java" target="_blank" rel="noopener">@Service</a> 注解的 Service 类们，将它们暴露成 Dubbo 服务提供者。</p><ul><li>注意⚠️：xml版的使用的是Spring的<code>@Service</code>，注解版使用的是 Dubbo自定义的 <code>@Service</code></li></ul><p>如此，我们就不需要使用 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.4 Dubbo XML 配置文件」</a> ，配置暴露的 Service 服务，而是通过 Dubbo 定义的 <code>@Service</code> 注解。</p><h3 id="3-2-3-UserRpcServiceImpl"><a href="#3-2-3-UserRpcServiceImpl" class="headerlink" title="3.2.3 UserRpcServiceImpl"></a>3.2.3 UserRpcServiceImpl</h3><p>创建 Dubbo Service 实现类。这里，我们创建 <code>UserRpcServiceImpl</code> 类，用户服务 RPC Service 实现类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcServiceImpl.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span>(version = <span class="string">"$&#123;dubbo.provider.UserRpcService.version&#125;"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserRpcServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserRpcService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> UserDTO <span class="title">get</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> UserDTO().setId(id)</span><br><span class="line">                .setName(<span class="string">"没有昵称："</span> + id)</span><br><span class="line">                <span class="comment">// 1 - 男；2 - 女 </span></span><br><span class="line">                .setGender(id % <span class="number">2</span> + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在类上，我们添加的是 Dubbo 定义的 <code>@Service</code> 注解。并且，在该注解里，我们可以添加该 Service 服务的配置。当然，每个属性和 <code>&lt;dubbo:service /&gt;</code> 标签是基本一致的。也因此，每个属性的说明，还可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service.html" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:service》</a> 。</li></ul><h3 id="3-2-4-ProviderApplication"><a href="#3-2-4-ProviderApplication" class="headerlink" title="3.2.4 ProviderApplication"></a>3.2.4 ProviderApplication</h3><p>创建 <code>ProviderApplication</code> 类，用于启动该项目，提供 Dubbo 服务。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ProviderApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProviderApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 启动 Spring Boot 应用</span></span><br><span class="line">        SpringApplication.run(ProviderApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在类上，无需添加 <code>@ImportResource</code> 注解，引入 <code>dubbo.xml</code> 配置文件。</li></ul><blockquote><p>后续的操作，和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.2.5 ProviderApplication」</a> 是一致的，这里就不重复赘述了。</p></blockquote><h2 id="3-3-Consumer"><a href="#3-3-Consumer" class="headerlink" title="3.3 Consumer"></a>3.3 Consumer</h2><p>对应 <code>labs-springboot2-dubbo-annotation-consumer</code> 项目，<strong>服务消费者</strong>，会调用 <code>labs-springboot2-dubbo-annotation-provider</code> 项目提供的 Dubbo Service 服务。</p><h3 id="3-3-1-引入依赖"><a href="#3-3-1-引入依赖" class="headerlink" title="3.3.1 引入依赖"></a>3.3.1 引入依赖</h3><blockquote><p>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.3.1 引入依赖」</a> 一致。</p></blockquote><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-annotation<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-annotation-consumer<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 引入定义的 Dubbo API 接口 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-annotation-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 引入 Spring Boot 依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 Dubbo 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.dubbo<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.4.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 使用 Zookeeper 作为注册中心 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-framework<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-recipes<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.13.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「3.2.1 引入依赖」</a> 一模一样，除了 <code>&lt;artifactId /&gt;</code> 改成了 <code>labs-springboot2-dubbo-annotation-consumer</code> 值。</li></ul><h3 id="3-3-2-应用配置文件"><a href="#3-3-2-应用配置文件" class="headerlink" title="3.3.2 应用配置文件"></a>3.3.2 应用配置文件</h3><blockquote><p>和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.3.2 应用配置文件」</a> 一致。</p></blockquote><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># dubbo 配置项，对应 DubboConfigurationProperties 配置类</span></span><br><span class="line"><span class="attr">dubbo:</span></span><br><span class="line">  <span class="comment"># Dubbo 应用配置</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">user-service-consumer</span> <span class="comment"># 应用名</span></span><br><span class="line">  <span class="comment"># Dubbo 注册中心配置</span></span><br><span class="line">  <span class="attr">registry:</span></span><br><span class="line">    <span class="attr">address:</span> <span class="string">zookeeper://127.0.0.1:2181</span> <span class="comment"># 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。</span></span><br><span class="line">  <span class="comment"># Dubbo 消费者配置</span></span><br><span class="line">  <span class="attr">consumer:</span></span><br><span class="line">    <span class="attr">timeout:</span> <span class="number">1000</span> <span class="comment"># 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改</span></span><br><span class="line">    <span class="attr">UserRpcService:</span></span><br><span class="line">      <span class="attr">version:</span> <span class="number">1.0</span><span class="number">.0</span></span><br></pre></td></tr></table></figure><h3 id="3-3-3-ConsumerApplication"><a href="#3-3-3-ConsumerApplication" class="headerlink" title="3.3.3 ConsumerApplication"></a>3.3.3 ConsumerApplication</h3><p>创建 <code>ConsumerApplication</code> 类，用于启动该项目，调用 Dubbo 服务。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ConsumerApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConsumerApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 启动 Spring Boot 应用</span></span><br><span class="line">				SpringApplication.run(ConsumerApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Component</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserRpcServiceTest</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(getClass());</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Reference</span>(version = <span class="string">"$&#123;dubbo.consumer.UserRpcService.version&#125;"</span>)</span><br><span class="line">        <span class="keyword">private</span> UserRpcService userRpcService;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            UserDTO user = userRpcService.get(<span class="number">1</span>);</span><br><span class="line">            logger.info(<span class="string">"[run][发起一次 Dubbo RPC 请求，获得用户为(&#123;&#125;)"</span>, user);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在类上，无需添加 <code>@ImportResource</code> 注解，引入 <code>dubbo.xml</code> 配置文件。</li><li>在 UserRpcServiceTest 中，我们使用 Dubbo 定义的 <code>@Reference</code> 注解，<strong>“直接”</strong>引用的 UserRpcService 服务对应的 UserRpcService Bean 。并且，在该注解里，我们可以添加该 Service 服务的配置。当然，每个属性和 `` 标签是基本一致的。也因此，每个属性的说明，还可见 <a href="http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-reference" target="_blank" rel="noopener">《Dubbo 文档 —— dubbo:reference》</a> 。</li></ul><blockquote><p>后续的操作，和 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2.3.4 ConsumerApplication」</a> 是一致的，这里就不重复赘述了。</p></blockquote><h2 id="3-4-选择注解还是-XML-配置？"><a href="#3-4-选择注解还是-XML-配置？" class="headerlink" title="3.4 选择注解还是 XML 配置？"></a>3.4 选择注解还是 XML 配置？</h2><p>卡姆个人倾向的话，偏向使用 <strong>XML 配置</strong>。</p><p>主要原因是，<code>@Reference</code> 注解，每次引用服务的时候，都需要在注解上添加好多配置的属性。这样，服务的引用的配置后就散落到各个类里了。</p><p>虽然说，我们可以把 <code>@Reference</code> 注解的配置的属性值，放到 <code>application.yaml</code> 等等配置文件里，但是如果我们要给<strong>相同 Service 的多个</strong> <code>@Reference</code> 增加新的配置属性时，就要每个注解都修改一遍。</p><p>对于这种情况，XML 配置的方式，只要修改一下该 Service 的 XML 配置，就可以全部生效了。</p><h1 id="4-参数验证"><a href="#4-参数验证" class="headerlink" title="4. 参数验证"></a>4. 参数验证</h1><p>参数校验，对于提供 API 调用的服务来说，必然是必不可少的。在 <a href="http://www.iocooder.cn/Spring-Boot/Validation/?self" target="_blank" rel="noopener">《芋道 Spring Boot 参数校验 Validation 入门》</a> 中，我们已经看了如何在 SpringMVC 和本地的 Service 使用参数校验的示例。</p><blockquote><p>参数验证功能是基于 <a href="https://jcp.org/en/jsr/detail?id=303" target="_blank" rel="noopener">JSR303</a> 实现的，用户只需标识 JSR303 标准的验证 annotation，并通过声明 filter 来实现验证。</p></blockquote><ul><li>我们在 <a href="http://www.iocoder.cn/Spring-Boot/Validation/?self" target="_blank" rel="noopener">《芋道 Spring Boot 参数校验 Validation 入门》</a> 中学习的，也是基于 JSR303 规范实现的，所以在使用上，是基本一致的。 有统一的规范，真好。</li></ul><p>下面，我们开始在 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「2. XML 配置」</a> 小节的 <a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-rpc/labs-springboot2-dubbo/labs-springboot2-dubbo-xml" target="_blank" rel="noopener">labs-springboot2-dubbo-xml</a> 示例项目，进行修改，添加参数校验的功能。</p><h2 id="4-1-API"><a href="#4-1-API" class="headerlink" title="4.1 API"></a>4.1 API</h2><p>本小节，我们来看看对 <code>labs-springboot2-dubbo-xml-api</code> 项目的改造。</p><h3 id="4-1-1-引入依赖"><a href="#4-1-1-引入依赖" class="headerlink" title="4.1.1 引入依赖"></a>4.1.1 引入依赖</h3><p>修改 <code>pom.xml</code> 文件中，引入相关依赖。</p><blockquote><p>部分版本号在父级pom中统一定义了</p></blockquote><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-dubbo-xml-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- JSR 参数校验规范 API --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>javax.validation<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>validation-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- JSR 参数校验规范实现，我们使用 hibernate-validator --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.hibernate.validator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hibernate-validator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 可能涉及到 EL 表达，所以引入，否则 hibernate-validator 在初始化会报错 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.glassfish<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>javax.el<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0.1-b11<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="4-1-2-UserAddDTO"><a href="#4-1-2-UserAddDTO" class="headerlink" title="4.1.2 UserAddDTO"></a>4.1.2 UserAddDTO</h3><p>创建 <code>UserAddDTO</code> 类，用户添加 DTO。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserAddDTO.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors</span>(chain = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserAddDTO</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 昵称</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@NotEmpty</span>(message = <span class="string">"昵称不能为空"</span>)</span><br><span class="line">    <span class="meta">@Length</span>(min = <span class="number">5</span>, max = <span class="number">16</span>, message = <span class="string">"账号长度为 5-16 位"</span>)</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 性别</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@NotNull</span>(message = <span class="string">"性别不能为空"</span>)</span><br><span class="line">    <span class="keyword">private</span> Integer gender;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在 <code>name</code> 和 <code>gender</code> 属性上，我们添加了参数校验的注解。</li></ul><h3 id="4-1-3-UserRpcService"><a href="#4-1-3-UserRpcService" class="headerlink" title="4.1.3 UserRpcService"></a>4.1.3 UserRpcService</h3><p>修改 <code>UserRpcService</code> 接口，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcService.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRpcService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据指定用户编号，获得用户信息</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 用户编号</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 用户信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">UserDTO <span class="title">get</span><span class="params">(@NotNull(message = <span class="string">"用户编号不能为空"</span>)</span> Integer id)</span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> ConstraintViolationException</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加新用户，返回新添加的用户编号</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> addDTO 添加的用户信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 用户编号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">Integer <span class="title">add</span><span class="params">(UserAddDTO addDTO)</span> <span class="keyword">throws</span> ConstraintViolationException</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在已有的 <code>#get(Integer id)</code> 方法上，添加 <code>@NotNull</code> 注解，校验用户编号不允许传空。</li><li>新增 <code>#add(UserAddDTO addDTO)</code> 方法，添加新用户，返回新添加的用户编号。我们已经在 UserAddDTO 类，添加了相应的参数校验的注解。</li><li>注意，因为参数校验不通过时，会抛出 ConstraintViolationException 异常，所以需要在接口的方法，显示使用 <code>throws</code> 注明。具体的原因，可以看看 <a href="https://blog.csdn.net/mj158518/article/details/51228649" target="_blank" rel="noopener">《浅谈 Dubbo 的 ExceptionFilter 异常处理》</a> 文章，了解下 Dubbo 的异常处理机制。</li></ul><h2 id="4-2-Provider"><a href="#4-2-Provider" class="headerlink" title="4.2 Provider"></a>4.2 Provider</h2><p>本小节，我们来看看对 <code>labs-springboot2-dubbo-xml-provider</code> 项目的改造。</p><h3 id="4-2-1-UserRpcServiceImpl"><a href="#4-2-1-UserRpcServiceImpl" class="headerlink" title="4.2.1 UserRpcServiceImpl"></a>4.2.1 UserRpcServiceImpl</h3><p>修改 <code>UserRpcServiceImpl</code> 类，简单实现下 <code>#add(UserAddDTO addDTO)</code> 方法。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcServiceImpl.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Integer <span class="title">add</span><span class="params">(UserAddDTO addDTO)</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 随便返回一个 id</span></span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">int</span>) (System.currentTimeMillis() / <span class="number">1000</span>); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-2-2-Dubbo-XML-配置文件"><a href="#4-2-2-Dubbo-XML-配置文件" class="headerlink" title="4.2.2 Dubbo XML 配置文件"></a>4.2.2 Dubbo XML 配置文件</h3><p>修改 <code>dubbo.xml</code> 配置文件，开启 UserRpcService 的参数校验功能。配置如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;dubbo:service ref&#x3D;&quot;userRpcServiceImpl&quot; interface&#x3D;&quot;cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService&quot;</span><br><span class="line">    version&#x3D;&quot;$&#123;dubbo.provider.UserRpcService.version&#125;&quot; validation&#x3D;&quot;true&quot; &#x2F;&gt;</span><br></pre></td></tr></table></figure><ul><li>这里，我们将 <code>validation</code> 设置为 <code>&quot;true&quot;</code> ，开启 Dubbo 服务<strong>提供者</strong>的 UserRpcService 服务的参数校验的功能。</li><li>如果胖友想把 Dubbo 服务提供者的所有 Service 服务的参数校验都开启，可以修改 <code>application.yaml</code> 配置文件，增加 <code>dubbo.provider.validation = true</code> 配置。</li></ul><h2 id="4-3-Consumer"><a href="#4-3-Consumer" class="headerlink" title="4.3 Consumer"></a>4.3 Consumer</h2><p>本小节，我们来看看对 <code>user-rpc-service-consumer</code> 项目的改造。</p><h3 id="4-3-1-Dubbo-XML-配置文件"><a href="#4-3-1-Dubbo-XML-配置文件" class="headerlink" title="4.3.1 Dubbo XML 配置文件"></a>4.3.1 Dubbo XML 配置文件</h3><p>修改 <code>dubbo.xml</code> 配置文件，开启 UserRpcService 的参数校验功能。配置如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;dubbo:reference id&#x3D;&quot;userService&quot; interface&#x3D;&quot;cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService&quot;</span><br><span class="line">                 version&#x3D;&quot;$&#123;dubbo.consumer.UserRpcService.version&#125;&quot; validation&#x3D;&quot;true&quot; &#x2F;&gt;</span><br></pre></td></tr></table></figure><ul><li>这里，我们将 <code>validation</code> 设置为 <code>&quot;true&quot;</code> ，开启 Dubbo 服务<strong>消费者</strong>的 UserRpcService 服务的参数校验的功能。</li><li>如果胖友想把 Dubbo 服务消费者的所有 Service 服务的参数校验都开启，可以修改 <code>application.yaml</code> 配置文件，增加 <code>dubbo.consumer.validation = true</code> 配置。</li></ul><p>可能胖友会有疑惑，服务提供者和服务消费者的 <code>validation = true</code> ，都是开启参数校验规则，会有什么区别呢？Dubbo 内置 <a href="https://github.com/apache/dubbo/blob/master/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java" target="_blank" rel="noopener">ValidationFilter</a> 过滤器，实现参数校验的功能，可作用于服务提供者和服务消费者。效果如下：</p><ul><li>如果服务<strong>消费者</strong>开启参数校验，请求参数校验不通过时，结束请求，抛出 ConstraintViolationException 异常。<strong>即，不会向服务提供者发起请求</strong>。</li><li>如果服务<strong>提供者</strong>开启参数校验，请求参数校验不通过时，结束请求，抛出 ConstraintViolationException 异常。<strong>即，不会执行后续的业务逻辑</strong>。</li></ul><p>实际项目在使用时，<strong>至少要开启服务提供者的参数校验功能</strong>。</p><h3 id="4-3-2-ConsumerApplication"><a href="#4-3-2-ConsumerApplication" class="headerlink" title="4.3.2 ConsumerApplication"></a>4.3.2 ConsumerApplication</h3><p>修改 <code>ConsumerApplication</code> 类，增加调用 UserRpcService 服务时，参数校验不通过的示例。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ConsumerApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserRpcServiceTest02</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> UserRpcService userRpcService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="comment">// 获得用户</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 发起调用</span></span><br><span class="line">            UserDTO user = userRpcService.get(<span class="keyword">null</span>); <span class="comment">// 故意传入空的编号，为了校验编号不通过</span></span><br><span class="line">            logger.info(<span class="string">"[run][发起一次 Dubbo RPC 请求，获得用户为(&#123;&#125;)]"</span>, user);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            logger.error(<span class="string">"[run][获得用户发生异常，信息为:[&#123;&#125;]"</span>, e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加用户</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 创建 UserAddDTO</span></span><br><span class="line">            UserAddDTO addDTO = <span class="keyword">new</span> UserAddDTO();</span><br><span class="line">          	<span class="comment">// 故意把名字打的特别长，为了校验名字不通过</span></span><br><span class="line">            addDTO.setName(<span class="string">"i am so handsome!handsome!handsome!"</span>); </span><br><span class="line">          	<span class="comment">// 不传递性别，为了校验性别不通过</span></span><br><span class="line">            addDTO.setGender(<span class="keyword">null</span>); </span><br><span class="line">            <span class="comment">// 发起调用</span></span><br><span class="line">            userRpcService.add(addDTO);</span><br><span class="line">            logger.info(<span class="string">"[run][发起一次 Dubbo RPC 请求，添加用户为(&#123;&#125;)]"</span>, addDTO);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            logger.error(<span class="string">"[run][添加用户发生异常，信息为:[&#123;&#125;]"</span>, e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>添加了两段代码，分别调用 UserRpcService 服务的 <code>#get(Integer id)</code> 和 <code>#add(UserAddDTO addDTO)</code> 方法，并且是参数不符合校验条件的示例。</li></ul><p>运行 <code>#main(String[] args)</code> 方法，启动项目。控制台打印日志如下：</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 调用 UserRpcService 服务的 `#get(Integer id)` 方法，参数不通过</span></span><br><span class="line"><span class="number">2020</span>-<span class="number">01</span>-<span class="number">07</span> <span class="number">12</span>:<span class="number">51</span>:<span class="number">42</span><span class="variable">.953</span> ERROR <span class="number">12320</span> --- [           main] ConsumerApplication$UserRpcServiceTest02 : [run][获得用户发生异常，信息为:[Failed to validate service: cn<span class="variable">.javadog</span><span class="variable">.labs</span><span class="variable">.springboot</span><span class="variable">.dubbo</span><span class="variable">.xml</span><span class="variable">.api</span><span class="variable">.UserRpcService</span>, method: get, cause: [ConstraintViolationImpl&#123;interpolatedMessage='用户编号不能为空', propertyPath=getArgument0, rootBeanClass=<span class="keyword">class</span> cn<span class="variable">.javadog</span><span class="variable">.labs</span><span class="variable">.springboot</span><span class="variable">.dubbo</span><span class="variable">.xml</span><span class="variable">.api</span><span class="variable">.UserRpcService_GetParameter_java</span><span class="variable">.lang</span><span class="variable">.Integer</span>, messageTemplate='用户编号不能为空'&#125;]]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 调用 UserRpcService 服务的 `#add(UserAddDTO addDTO)` 方法，参数不通过</span></span><br><span class="line"><span class="number">2020</span>-<span class="number">01</span>-<span class="number">07</span> <span class="number">12</span>:<span class="number">51</span>:<span class="number">42</span><span class="variable">.969</span> ERROR <span class="number">12320</span> --- [           main] ConsumerApplication$UserRpcServiceTest02 : [run][添加用户发生异常，信息为:[Failed to validate service: cn<span class="variable">.javadog</span><span class="variable">.labs</span><span class="variable">.springboot</span><span class="variable">.dubbo</span><span class="variable">.xml</span><span class="variable">.api</span><span class="variable">.UserRpcService</span>, method: add, cause: [ConstraintViolationImpl&#123;interpolatedMessage='账号长度为 <span class="number">5</span>-<span class="number">16</span> 位', propertyPath=name, rootBeanClass=<span class="keyword">class</span> cn<span class="variable">.javadog</span><span class="variable">.labs</span><span class="variable">.springboot</span><span class="variable">.dubbo</span><span class="variable">.xml</span><span class="variable">.dto</span><span class="variable">.UserAddDTO</span>, messageTemplate='账号长度为 <span class="number">5</span>-<span class="number">16</span> 位'&#125;, ConstraintViolationImpl&#123;interpolatedMessage='性别不能为空', propertyPath=gender, rootBeanClass=<span class="keyword">class</span> cn<span class="variable">.javadog</span><span class="variable">.labs</span><span class="variable">.springboot</span><span class="variable">.dubbo</span><span class="variable">.xml</span><span class="variable">.dto</span><span class="variable">.UserAddDTO</span>, messageTemplate='性别不能为空'&#125;]]</span><br></pre></td></tr></table></figure><ul><li>上述贼长的两段日志，我们可以看到两次 UserRpcService 服务的调用，都抛出了 ConstraintViolationException 异常。</li></ul><h2 id="4-4-存在的问题"><a href="#4-4-存在的问题" class="headerlink" title="4.4 存在的问题"></a>4.4 存在的问题</h2><p>如果我们关闭掉服务消费者的参数校验功能，而<strong>只使用服务提供者的参数校验功能</strong>的情况下，当参数校验不通过时，因为 Hibernate <a href="https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ConstraintDescriptorImpl.java" target="_blank" rel="noopener">ConstraintDescriptorImpl</a> 没有<strong>默认</strong>空构造方法，所以 Hessian 反序列化时，会抛出 HessianProtocolException 异常。详细如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Caused by: com.alibaba.com.caucho.hessian.io.HessianProtocolException: &#39;org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl&#39; could not be instantiated</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:316)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:201)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2818)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2145)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2074)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2118)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2074)</span><br><span class="line">	at com.alibaba.com.caucho.hessian.io.JavaDeserializer$ObjectFieldDeserializer.deserialize(JavaDeserializer.java:406)</span><br></pre></td></tr></table></figure><p>目前有两种解决方案：</p><ul><li>方案一，不要关闭掉服务消费者的参数校验功能。</li><li>方案二，参考 <a href="https://cloud.tencent.com/developer/article/1472816" target="_blank" rel="noopener">《Dubbo 使用 JSR303 框架 hibernate-validator 遇到 ConstraintDescriptorImpl could not be instantiated》</a> 文章的方法三。</li><li>方案三，Service 接口上，不要抛出 ConstraintViolationException 异常。这样，该异常就可以被 Dubbo 内置的 ExceptionFilter 封装成 RuntimeException 异常，就不会存在反序列化的问题。</li></ul><p>不过目前方案二，提交在 <a href="https://github.com/apache/incubator-dubbo/pull/1708" target="_blank" rel="noopener">https://github.com/apache/incubator-dubbo/pull/1708</a> 的 PR 代码，已经被 Dubbo 开发团队否决了。所以，目前建议还是采用<strong>方案一</strong>来解决。</p><h1 id="5-自定义实现拓展点"><a href="#5-自定义实现拓展点" class="headerlink" title="5. 自定义实现拓展点"></a>5. 自定义实现拓展点</h1><p>在<a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「4. 参数校验」</a> 小节中，我们入门了 Dubbo 提供的参数校验的功能，它是由 <a href="https://github.com/apache/dubbo/blob/master/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java" target="_blank" rel="noopener">ValidationFilter</a> 过滤器，通过拦截请求，根据我们添加 JSR303 定义的注解，校验参数是否正确。在 Dubbo 框架中，还提供了 <a href="https://github.com/apache/dubbo/blob/master/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java" target="_blank" rel="noopener">AccessLogFilter</a>、<a href="https://github.com/apache/dubbo/blob/master/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java" target="_blank" rel="noopener">ExceptionFilter</a> 等等过滤器，他们都属于 Dubbo <a href="https://github.com/apache/dubbo/blob/master/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java" target="_blank" rel="noopener">Filter</a> 接口的实现类。</p><p>而实际上，Filter 是 Dubbo 定义的 <a href="http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html" target="_blank" rel="noopener">调用拦截</a> 拓展点。除了 Filter 拓展点，Dubbo 还定义了 <a href="http://dubbo.apache.org/zh-cn/docs/dev/impls/protocol.html" target="_blank" rel="noopener">协议</a>、<a href="http://dubbo.apache.org/zh-cn/docs/dev/impls/router.html" target="_blank" rel="noopener">路由</a>、<a href="http://dubbo.apache.org/zh-cn/docs/dev/impls/registry.html" target="_blank" rel="noopener">注册中心</a> 等等拓展点。如下图所示：<img src="4.png" alt="拓展点"></p><blockquote><p>FROM <a href="http://dubbo.apache.org/zh-cn/docs/dev/SPI.html" target="_blank" rel="noopener">《Dubbo 扩展点加载》</a></p><p>Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。</p><p>Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。</p><p>Dubbo 改进了 JDK 标准的 SPI 的以下问题：</p><ul><li>JDK 标准的 SPI 会一次性实例化扩展点所有实现，如果有扩展实现初始化很耗时，但如果没用上也加载，会很浪费资源。</li><li>如果扩展点加载失败，连扩展点的名称都拿不到了。比如：JDK 标准的 ScriptEngine，通过 <code>getName()</code> 获取脚本类型的名称，但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在，导致 RubyScriptEngine 类加载失败，这个失败原因被吃掉了，和 ruby 对应不起来，当用户执行 ruby 脚本时，会报不支持 ruby，而不是真正失败的原因。</li><li>增加了对扩展点 IoC 和 AOP 的支持，一个扩展点可以直接 setter 注入其它扩展点。</li></ul></blockquote><p>下面，我们实现一个对 <a href="https://github.com/apache/dubbo/blob/master/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java" target="_blank" rel="noopener">ExceptionFilter</a> 增强的过滤器，实现即使 Service API 接口上，未定义 ServiceException、ConstraintViolationException 等异常，也不会自动封装成 RuntimeException 。😈 毕竟，要求每个开发同学记得在 Service API 接口上，添加 ServiceException、ConstraintViolationException 等异常，是挺困难的事情，总是可能不经意遗忘。</p><blockquote><p>艿艿：关于本小节的内容，卡姆希望胖友有看过 <a href="https://calm_java.gitee.io/blog/2019/12/29/labs-springboot2-SpringMVC/">《Spring Boot SpringMVC 入门》</a> 的 <a href="https://calm_java.gitee.io/blog/2019/12/29/labs-springboot2-SpringMVC/">「4. 全局统一返回」</a> 和 <a href="https://calm_java.gitee.io/blog/2019/12/29/labs-springboot2-SpringMVC/">「5. 全局异常处理」</a> 小节的内容，因为涉及到的思路是一致的。</p></blockquote><h2 id="5-1-API"><a href="#5-1-API" class="headerlink" title="5.1 API"></a>5.1 API</h2><p>本小节，我们来看看对 <code>labs-springboot2-dubbo-xml-api</code> 项目的改造。</p><h3 id="5-1-1-ServiceExceptionEnum"><a href="#5-1-1-ServiceExceptionEnum" class="headerlink" title="5.1.1 ServiceExceptionEnum"></a>5.1.1 ServiceExceptionEnum</h3><p>创建 <code>ServiceExceptionEnum</code> 枚举类，枚举项目中的错误码。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ServiceExceptionEnum.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> ServiceExceptionEnum &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ========== 系统级别 ==========</span></span><br><span class="line">    SUCCESS(<span class="number">0</span>, <span class="string">"成功"</span>),</span><br><span class="line">    SYS_ERROR(<span class="number">2001001000</span>, <span class="string">"服务端发生异常"</span>),</span><br><span class="line">    MISSING_REQUEST_PARAM_ERROR(<span class="number">2001001001</span>, <span class="string">"参数缺失"</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ========== 用户模块 ==========</span></span><br><span class="line">    USER_NOT_FOUND(<span class="number">1001002000</span>, <span class="string">"用户不存在"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 错误码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> code;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 错误提示</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String message;</span><br><span class="line"></span><br><span class="line">    ServiceExceptionEnum(<span class="keyword">int</span> code, String message) &#123;</span><br><span class="line">        <span class="keyword">this</span>.code = code;</span><br><span class="line">        <span class="keyword">this</span>.message = message;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ... 省略 getting 方法</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>因为错误码是全局的，最好按照模块来拆分。如下是「芋道源码」在 <a href="https://github.com/YunaiV/onemall" target="_blank" rel="noopener">onemall</a> 项目的实践：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line"> * 服务异常</span><br><span class="line"> *</span><br><span class="line"> * 参考 https:&#x2F;&#x2F;www.kancloud.cn&#x2F;onebase&#x2F;ob&#x2F;484204 文章</span><br><span class="line"> *</span><br><span class="line"> * 一共 10 位，分成四段</span><br><span class="line"> *</span><br><span class="line"> * 第一段，1 位，类型</span><br><span class="line"> *      1 - 业务级别异常</span><br><span class="line"> *      2 - 系统级别异常</span><br><span class="line"> * 第二段，3 位，系统类型</span><br><span class="line"> *      001 - 用户系统</span><br><span class="line"> *      002 - 商品系统</span><br><span class="line"> *      003 - 订单系统</span><br><span class="line"> *      004 - 支付系统</span><br><span class="line"> *      005 - 优惠劵系统</span><br><span class="line"> *      ... - ...</span><br><span class="line"> * 第三段，3 位，模块</span><br><span class="line"> *      不限制规则。</span><br><span class="line"> *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：</span><br><span class="line"> *          001 - OAuth2 模块</span><br><span class="line"> *          002 - User 模块</span><br><span class="line"> *          003 - MobileCode 模块</span><br><span class="line"> * 第四段，3 位，错误码</span><br><span class="line"> *       不限制规则。</span><br><span class="line"> *       一般建议，每个模块自增。</span><br><span class="line"> *&#x2F;</span><br></pre></td></tr></table></figure></li></ul><h3 id="5-1-2-ServiceException"><a href="#5-1-2-ServiceException" class="headerlink" title="5.1.2 ServiceException"></a>5.1.2 ServiceException</h3><p>创建 <code>ServiceException</code> 异常类，继承 RuntimeException 异常类，用于定义业务异常。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ServiceException</span> <span class="keyword">extends</span> <span class="title">RuntimeException</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 错误码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Integer code;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ServiceException</span><span class="params">()</span> </span>&#123; <span class="comment">// 创建默认构造方法，用于反序列化的场景。</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ServiceException</span><span class="params">(ServiceExceptionEnum serviceExceptionEnum)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 使用父类的 message 字段</span></span><br><span class="line">        <span class="keyword">super</span>(serviceExceptionEnum.getMessage());</span><br><span class="line">        <span class="comment">// 设置错误码</span></span><br><span class="line">        <span class="keyword">this</span>.code = serviceExceptionEnum.getCode();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ServiceException</span><span class="params">(ServiceExceptionEnum serviceExceptionEnum, String message)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 使用父类的 message 字段</span></span><br><span class="line">        <span class="keyword">super</span>(message);</span><br><span class="line">        <span class="comment">// 设置错误码</span></span><br><span class="line">        <span class="keyword">this</span>.code = serviceExceptionEnum.getCode();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Integer <span class="title">getCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> code;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="5-2-Provider"><a href="#5-2-Provider" class="headerlink" title="5.2 Provider"></a>5.2 Provider</h2><p>本小节，我们来看看对 <code>labs-springboot2-dubbo-xml-provider</code> 项目的改造。</p><h3 id="5-2-1-DubboExceptionFilter"><a href="#5-2-1-DubboExceptionFilter" class="headerlink" title="5.2.1 DubboExceptionFilter"></a>5.2.1 DubboExceptionFilter</h3><p>创建 <code>DubboExceptionFilter</code>，继承 <a href="https://github.com/apache/dubbo/blob/master/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java" target="_blank" rel="noopener">ListenableFilter</a> 抽象类，实现对 ExceptionFilter 增强的过滤器。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DubboExceptionFilter.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Activate</span>(group = CommonConstants.PROVIDER)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DubboExceptionFilter</span> <span class="keyword">extends</span> <span class="title">ListenableFilter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DubboExceptionFilter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.listener = <span class="keyword">new</span> ExceptionListenerX();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Result <span class="title">invoke</span><span class="params">(Invoker&lt;?&gt; invoker, Invocation invocation)</span> <span class="keyword">throws</span> RpcException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> invoker.invoke(invocation);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ExceptionListenerX</span> <span class="keyword">extends</span> <span class="title">ExceptionListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(Result appResponse, Invoker&lt;?&gt; invoker, Invocation invocation)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">"i`m working!"</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 发生异常，并且非泛化调用</span></span><br><span class="line">            <span class="keyword">if</span> (appResponse.hasException() &amp;&amp; GenericService<span class="class">.<span class="keyword">class</span> !</span>= invoker.getInterface()) &#123;</span><br><span class="line">                Throwable exception = appResponse.getException();</span><br><span class="line">                <span class="comment">// 如果是 ServiceException 异常，直接返回</span></span><br><span class="line">                <span class="keyword">if</span> (exception <span class="keyword">instanceof</span> ServiceException) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 如果是参数校验的 ConstraintViolationException 异常，则封装返回</span></span><br><span class="line">                <span class="keyword">if</span> (exception <span class="keyword">instanceof</span> ConstraintViolationException) &#123;</span><br><span class="line">                    appResponse.setException(<span class="keyword">this</span>.handleConstraintViolationException((ConstraintViolationException) exception));</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 其它情况，继续使用父类处理</span></span><br><span class="line">            <span class="keyword">super</span>.onResponse(appResponse, invoker, invocation);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> ServiceException <span class="title">handleConstraintViolationException</span><span class="params">(ConstraintViolationException ex)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// 拼接错误</span></span><br><span class="line">            StringBuilder detailMessage = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">            <span class="keyword">for</span> (ConstraintViolation&lt;?&gt; constraintViolation : ex.getConstraintViolations()) &#123;</span><br><span class="line">                <span class="comment">// 使用 ; 分隔多个错误</span></span><br><span class="line">                <span class="keyword">if</span> (detailMessage.length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    detailMessage.append(<span class="string">";"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 拼接内容到其中</span></span><br><span class="line">                detailMessage.append(constraintViolation.getMessage());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 返回异常</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> ServiceException(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR,</span><br><span class="line">                    detailMessage.toString());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ExceptionListener</span> <span class="keyword">implements</span> <span class="title">Listener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> Logger logger = LoggerFactory.getLogger(ExceptionListener<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(Result appResponse, Invoker&lt;?&gt; invoker, Invocation invocation)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (appResponse.hasException() &amp;&amp; GenericService<span class="class">.<span class="keyword">class</span> !</span>= invoker.getInterface()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Throwable exception = appResponse.getException();</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 如果是检查异常，直接抛出</span></span><br><span class="line">                    <span class="keyword">if</span> (!(exception <span class="keyword">instanceof</span> RuntimeException) &amp;&amp; (exception <span class="keyword">instanceof</span> Exception)) &#123;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">// 如果是方法签名上的异常，直接抛出</span></span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());</span><br><span class="line">                        Class&lt;?&gt;[] exceptionClassses = method.getExceptionTypes();</span><br><span class="line">                        <span class="keyword">for</span> (Class&lt;?&gt; exceptionClass : exceptionClassses) &#123;</span><br><span class="line">                            <span class="keyword">if</span> (exception.getClass().equals(exceptionClass)) &#123;</span><br><span class="line">                                <span class="keyword">return</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (NoSuchMethodException e) &#123;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 如果方法签名上也找不到这个异常，就打印错误日志</span></span><br><span class="line">                    logger.error(<span class="string">"Got unchecked and undeclared exception which called by "</span> + RpcContext.getContext().getRemoteHost() + <span class="string">". service: "</span> + invoker.getInterface().getName() + <span class="string">", method: "</span> + invocation.getMethodName() + <span class="string">", exception: "</span> + exception.getClass().getName() + <span class="string">": "</span> + exception.getMessage(), exception);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 如果异常和service服务在同一个jar包下，直接抛出，也就是service接口和异常都自定义在同一个模块，如API模块</span></span><br><span class="line">                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());</span><br><span class="line">                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());</span><br><span class="line">                    <span class="keyword">if</span> (serviceFile == <span class="keyword">null</span> || exceptionFile == <span class="keyword">null</span> || serviceFile.equals(exceptionFile)) &#123;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">// 如果是jdk的异常，也直接抛出</span></span><br><span class="line">                    String className = exception.getClass().getName();</span><br><span class="line">                    <span class="keyword">if</span> (className.startsWith(<span class="string">"java."</span>) || className.startsWith(<span class="string">"javax."</span>)) &#123;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">// 如果是dubbo的异常，也直接抛出</span></span><br><span class="line">                    <span class="keyword">if</span> (exception <span class="keyword">instanceof</span> RpcException) &#123;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 对于其它的，包起来，返回客户端</span></span><br><span class="line">                    appResponse.setException(<span class="keyword">new</span> RuntimeException(StringUtils.toString(exception)));</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">                    logger.warn(<span class="string">"Fail to ExceptionFilter when called by "</span> + RpcContext.getContext().getRemoteHost() + <span class="string">". service: "</span> + invoker.getInterface().getName() + <span class="string">", method: "</span> + invocation.getMethodName() + <span class="string">", exception: "</span> + e.getClass().getName() + <span class="string">": "</span> + e.getMessage(), e);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onError</span><span class="params">(Throwable e, Invoker&lt;?&gt; invoker, Invocation invocation)</span> </span>&#123;</span><br><span class="line">            logger.error(<span class="string">"Got unchecked and undeclared exception which called by "</span> + RpcContext.getContext().getRemoteHost() + <span class="string">". service: "</span> + invoker.getInterface().getName() + <span class="string">", method: "</span> + invocation.getMethodName() + <span class="string">", exception: "</span> + e.getClass().getName() + <span class="string">": "</span> + e.getMessage(), e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 用于测试</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setLogger</span><span class="params">(Logger logger)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.logger = logger;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在类上，添加 <a href="https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Activate.java" target="_blank" rel="noopener">@Activate</a> 注解，并设置 <code>&quot;group = CommonConstants.PROVIDER&quot;</code> 属性，将 DubboExceptionFilter 过滤器仅在<strong>服务提供者</strong>生效。</li><li>因为目前 Dubbo 源码改版，建议在对于 Filter 拓展点的实现，继承 ListenableFilter 抽象类，更简易的实现对调用结果的处理。</li><li>在构造方法中，我们创建了 ExceptionListenerX 类，作为 <code>listener</code>监听器。而 ExceptionListenerX 继承自的 ExceptionListener 类，是我们直接从 Dubbo <a href="https://github.com/apache/dubbo/blob/master/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java#L55-L113" target="_blank" rel="noopener">ExceptionFilter.ExceptionListener</a> 复制过来的逻辑，为了保持 ExceptionFilter <strong>原有逻辑的不变</strong> 。下面，让我们来看看 ExceptionListenerX 的实现代码：<ul><li><code>&lt;1&gt;</code> 处，如果是 ServiceException 异常，直接返回。</li><li><code>&lt;2&gt;</code> 处，如果是参数校验的 ConstraintViolationException 异常，则调用 <code>#handleConstraintViolationException(ConstraintViolationException ex)</code> 方法，封装成 ServiceException 异常，之后返回。</li><li><code>&lt;3&gt;</code> 处，其它情况，继续使用父类 ExceptionListener 来处理。</li></ul></li></ul><p>这里，可能有胖友对 ExceptionFilter 异常处理不是很了解，建议看看 <a href="https://blog.csdn.net/mj158518/article/details/51228649" target="_blank" rel="noopener">《浅谈 Dubbo 的 ExceptionFilter 异常处理》</a> 文章。</p><p>另外，DubboExceptionFilter 是 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/">「4.4 存在问题」</a> 的方案二的一种变种解决方案。</p><h3 id="5-2-2-UserRpcServiceImpl"><a href="#5-2-2-UserRpcServiceImpl" class="headerlink" title="5.2.2 UserRpcServiceImpl"></a>5.2.2 UserRpcServiceImpl</h3><p>修改 <code>UserRpcServiceImpl</code> 类，修改下 <code>#add(UserAddDTO addDTO)</code> 方法，抛出 ServiceException 异常。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRpcServiceImpl.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Integer <span class="title">add</span><span class="params">(UserAddDTO addDTO)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 这里，模拟用户已经存在的情况</span></span><br><span class="line">    <span class="keyword">if</span> (ERROR_NAME.equals(addDTO.getName())) &#123;</span><br><span class="line">    		<span class="keyword">throw</span> <span class="keyword">new</span> ServiceException(ServiceExceptionEnum.USER_EXISTS);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 随便返回一个 id</span></span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">int</span>) (System.currentTimeMillis() / <span class="number">1000</span>); <span class="comment">// 嘿嘿，随便返回一个 id</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="5-2-3-添加SPI文件"><a href="#5-2-3-添加SPI文件" class="headerlink" title="5.2.3 添加SPI文件"></a>5.2.3 添加SPI文件</h3><p>在 <code>resources</code> 下创建包 <code>META-INF/dubbo</code>，再在该包下创建文件，命名为 <code>org.apache.dubbo.rpc.Filter</code>，这个名称实际就是你自定义的<code>filter</code> 所实现的顶层接口，文件内容如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dubboExceptionFilter&#x3D;cn.javadog.labs.springboot.dubbo.xml.filter.DubboExceptionFilter</span><br></pre></td></tr></table></figure><ul><li>有多个实现，换行添加即可</li><li>key为别名，value为实现类全路径。key可以用在需要指定扩展点的地方，如配置文件中，``dubbo.provider.filter=-dubboExceptionFilter`</li><li>注意⚠️：虽然dubbo向下兼容，即文件名命名为<code>com.alibaba.dubbo.rpc.Filter</code>，但只是之前的版本，目前dubbo已经被Apache升级为顶级项目，务必使用 <code>org.apache.dubbo.rpc.Filter</code>。</li></ul><h3 id="5-2-4-应用配置文件"><a href="#5-2-4-应用配置文件" class="headerlink" title="5.2.4 应用配置文件"></a>5.2.4 应用配置文件</h3><p>修改 <code>application.yml</code> 配置文件，添加 <code>dubbo.provider.filter=-exception</code> 配置项，去掉服务提供者的 ExceptionFilter 过滤器。</p><ul><li>Dubbo 会自动扫描 <code>/META-INF/dubbo/org.apache.dubbo.rpc.Filter</code> 内的配置，不需要再次通过<code>dubbo.provider.filter=filterName</code>，当你需要去除某个扩展点时，再去配置即可，如 <code>dubbo.provider.filter=-filterName</code>，需要注意的是，如果去除多个，如<code>-a,-b</code>，中间不要添加空格，会报错！</li></ul><p>如果胖友仅仅想去掉 UserRpcService 服务的 ExceptionFilter 过滤器，可以修改 <code>dubbo.xml</code>配置文件，配置如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dubbo:service</span> <span class="attr">ref</span>=<span class="string">"userRpcServiceImpl"</span> <span class="attr">interface</span>=<span class="string">"cn.javadog.labs.springboot.dubbo.xml.api.UserRpcService"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">version</span>=<span class="string">"$&#123;dubbo.provider.UserRpcService.version&#125;"</span> <span class="attr">validation</span>=<span class="string">"true"</span> <span class="attr">filter</span>=<span class="string">"-exception"</span> /&gt;</span></span><br></pre></td></tr></table></figure><ul><li>这里，我们将 <code>filter</code> 设置为 <code>&quot;-exception&quot;</code> ，去掉服务提供者的 UserRpcService 的 ExceptionFilter 过滤器。</li></ul><p>当然，一般情况下啊，我们采用全局配置，即通过 <code>dubbo.provider.filter=-exception</code> 配置项。</p><h2 id="5-3-Consumer"><a href="#5-3-Consumer" class="headerlink" title="5.3 Consumer"></a>5.3 Consumer</h2><p>本小节，我们来看看对 <code>labs-springboot2-dubbo-xml-consumer</code> 项目的改造。</p><h3 id="5-3-1-ConsumerApplication"><a href="#5-3-1-ConsumerApplication" class="headerlink" title="5.3.1 ConsumerApplication"></a>5.3.1 ConsumerApplication</h3><p>修改 <code>ConsumerApplication</code> 类，增加调用 UserRpcService 服务时，抛出 ServiceException 异常的示例。代码如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ConsumerApplication.java</span></span><br><span class="line"></span><br><span class="line">@Component</span><br><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">UserRpcServiceTest03</span> <span class="title">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    private final Logger logger = LoggerFactory.getLogger(getClass());</span><br><span class="line"></span><br><span class="line">    @Resource</span><br><span class="line">    private UserRpcService userRpcService;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public <span class="keyword">void</span> run(<span class="built_in">String</span>... args) &#123;</span><br><span class="line">        <span class="comment">// 添加用户</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 创建 UserAddDTO</span></span><br><span class="line">            UserAddDTO addDTO = <span class="keyword">new</span> UserAddDTO();</span><br><span class="line">            <span class="comment">// 设置为 ERROR_NAME ，触发 ServiceException 异常</span></span><br><span class="line">            addDTO.setName(UserRpcService.ERROR_NAME);</span><br><span class="line">            addDTO.setGender(<span class="number">1</span>);</span><br><span class="line">            <span class="comment">// 发起调用</span></span><br><span class="line">            userRpcService.add(addDTO);</span><br><span class="line">            logger.info(<span class="string">"[run][发起一次 Dubbo RPC 请求，添加用户为(&#123;&#125;)]"</span>, addDTO);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            logger.error(<span class="string">"[run][添加用户发生异常(&#123;&#125;)，信息为:[&#123;&#125;]"</span>, e.getClass().getSimpleName(), e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>添加了一段代码，调用 UserRpcService 服务的<code>#add(UserAddDTO addDTO)</code> 方法，并且是抛出 ServiceException 异常的示例。</li></ul><p>运行 <code>#main(String[] args)</code> 方法，启动项目。控制台打印日志如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2020-01-07 13:52:09.466 ERROR 12556 --- [           main] ConsumerApplication$UserRpcServiceTest03 : [run][添加用户发生异常(ServiceException)，信息为:[用户已存在]</span><br></pre></td></tr></table></figure><ul><li>我们可以看到，成功抛出 ServiceException 异常，即使我们在 UserRpcService API 接口的 <code>#add(UserAddDTO addDTO)</code> 方法上，并未显示 <code>throws</code> 抛出 UserRpcService 异常。</li></ul><h2 id="5-4-小结"><a href="#5-4-小结" class="headerlink" title="5.4 小结"></a>5.4 小结</h2><p>实际上，因为我们把 ServiceException 放在了 Service API 所在的 Maven 项目里，所以即使使用 Dubbo 内置的 ExceptionFilter 过滤器，并且 UserRpcService API 接口的 <code>#add(UserAddDTO addDTO)</code> 方法并未显示 <code>throws</code> 抛出 UserRpcService 异常，ExceptionFilter 也不会把 UserRpcService 封装成 RuntimeException 异常。咳咳咳 如果不了解的胖友，胖友在回看下 <a href="https://blog.csdn.net/mj158518/article/details/51228649" target="_blank" rel="noopener">《浅谈 Dubbo 的 ExceptionFilter 异常处理》</a> 文章，结尾的“4. 把异常放到 provider-api 的 jar 包中”。这部分有疑问的留言，卡姆会及时答复！</p><p>实际项目的 ExceptionFilter 增强封装，可以看看「芋道源码」在开源项目 <a href="https://github.com/YunaiV/onemall/" target="_blank" rel="noopener">onemall</a> 中，会把 <a href="https://github.com/YunaiV/onemall/blob/master/common/common-framework/src/main/java/cn/iocoder/common/framework/exception/ServiceException.java" target="_blank" rel="noopener">ServiceException</a> 和 <a href="https://github.com/YunaiV/onemall/blob/master/common/common-framework/src/main/java/cn/iocoder/common/framework/dubbo/DubboExceptionFilter.java" target="_blank" rel="noopener">DubboExceptionFilter</a> 放在 <a href="https://github.com/YunaiV/onemall/tree/master/common" target="_blank" rel="noopener">common-framework</a> <strong>框架项目</strong>中，而不是<strong>各个业务项目</strong>中。</p><h1 id="666-彩蛋"><a href="#666-彩蛋" class="headerlink" title="666. 彩蛋"></a>666. 彩蛋</h1><p>现在，Dubbo 可以说从原本的 Java RPC 框架，演化成 Dubbo 生态体系，其周边也越来越丰富。所以，让我们一起来期望 <a href="https://www.infoq.cn/article/baf67mzkQoj6-b7CoX31" target="_blank" rel="noopener">《Dubbo 3.0 预览版详细解读，关注异步化和响应式编程》</a> 。</p><p>无意中，发现 Dubbo 官方已经整理了 Dubbo 的整个生态体系，具体可以看看 <a href="http://dubbo.apache.org/en-us/ecology/index.html" target="_blank" rel="noopener">Build production-ready microservices</a> 页面。咳咳咳，真特喵的齐全，完全学不动了。</p><p>另外，有一点需要提醒下，很多初学 Dubbo 的胖友，可能会犯跟卡姆一样的错误，直接把原本的 Service 层，直接接入 Dubbo 框架，提供 Dubbo Service RPC 调用。使用xml配置直接接入没有问题，但使用注解配置就会有问题，同时加两个不同的@service会很难看，而且会造成事务问题(我没真正测试过，哈哈)，建议使用注解的话，重新写个service调用业务service。</p><p>因为本文仅仅是在 Spring Boot 下使用 Dubbo RPC 框架的入门文章，这里在推荐一些不错的内容：</p><ul><li><a href="http://dubbo.apache.org/zh-cn/docs/user/quick-start.html" target="_blank" rel="noopener">《Dubbo 官方文档》</a> ：还有比官方文档更香的东西么？在国内的开源项目中，Dubbo 的文档质量，起码 TOP10 吧。</li><li><a href="https://github.com/apache/dubbo-samples/tree/master/java" target="_blank" rel="noopener">dubbo-samples</a> 仓库：提供了大量的示例，美滋滋。</li><li><a href="http://www.iocoder.cn/RPC/laoxu/rpc-interface-design/?self" target="_blank" rel="noopener">《设计 RPC 接口时，你有考虑过这些吗？》</a> ：让你更优雅的设计 RPC 接口。</li><li><a href="http://www.iocoder.cn/Fight/How-should-your-project-be-properly-layered/?self" target="_blank" rel="noopener">《你的项目应该如何正确分层？》</a> ：可以借鉴的项目分层。</li></ul></div><div class="article-footer"><blockquote class="mt-2x"><ul class="post-copyright list-unstyled"><li class="post-copyright-link hidden-xs"><strong>本文链接：</strong> <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/" title="Spring Boot Dubbo 入门" target="_blank" rel="external">https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-dubbo/</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external">CC BY 4.0 CN协议</a> 许可协议。转载请注明出处！</li></ul></blockquote><div class="panel panel-default panel-badger"><div class="panel-body"><figure class="media"><div class="media-left"><a href="https://github.com/yuyong725" target="_blank" class="img-burn thumb-sm visible-lg"><img src="/blog/images/avatar.jpg" class="img-rounded w-full" alt=""></a></div><div class="media-body"><h3 class="media-heading"><a href="https://github.com/yuyong725" target="_blank"><span class="text-dark">卡姆</span><small class="ml-1x">a Java coder need calm</small></a></h3><div>享受编程</div></div></figure></div></div></div></article><section id="comments"></section></div><nav class="bar bar-footer clearfix" data-stick-bottom><div class="bar-inner"><ul class="pager pull-left"><li class="prev"><a href="/blog/2020/01/07/labs-springboot2-jdbctemplate/" title="Spring Boot JdbcTemplate 入门"><i class="icon icon-angle-left" aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a></li><li class="next"><a href="/blog/2020/01/05/OAuth2-guide-pdf/" title="OAuth2.0最简向导"><span>下一篇&nbsp;&nbsp;</span><i class="icon icon-angle-right" aria-hidden="true"></i></a></li><li class="toggle-toc"><a class="toggle-btn" data-toggle="collapse" href="#collapseToc" aria-expanded="true" title="文章目录" role="button"><span>[&nbsp;</span><span>文章目录</span> <i class="text-collapsed icon icon-anchor"></i> <i class="text-in icon icon-close"></i> <span>]</span></a></li></ul><button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal" data-target="#donateModal"><span>赏</span></button><div class="bar-right"><div class="share-component" data-sites="wechat" data-mobile-sites="wechat"></div></div></div></nav><div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content donate"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><div class="modal-body"><div class="donate-box"><div class="donate-head"><p>感谢您的支持，我会继续努力的!</p></div><div class="tab-content"><div role="tabpanel" class="tab-pane fade active in" id="alipay"><div class="donate-payimg"><img src="/blog/images/donate/alipayimg.jpeg" alt="扫码支持" title="扫一扫"></div><p class="text-muted mv">扫码打赏，你说多少就多少</p><p class="text-grey">打开支付宝扫一扫，即可进行扫码打赏哦</p></div><div role="tabpanel" class="tab-pane fade" id="wechatpay"><div class="donate-payimg"><img src="/blog/images/donate/wechatpayimg.jpeg" alt="扫码支持" title="扫一扫"></div><p class="text-muted mv">扫码打赏，你说多少就多少</p><p class="text-grey">打开微信扫一扫，即可进行扫码打赏哦</p></div></div><div class="donate-footer"><ul class="nav nav-tabs nav-justified" role="tablist"><li role="presentation" class="active"><a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay" aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a></li><li role="presentation"><a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab" aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i> 微信支付</a></li></ul></div></div></div></div></div></div></main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter"></footer><script src="//cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script><script>window.jQuery||document.write('<script src="js/jquery.min.js"><\/script>')</script><script src="/blog/js/plugin.min.js"></script><script src="/blog/js/application.js"></script><script>!function(T){var N={TRANSLATION:{POSTS:"文章",PAGES:"页面",CATEGORIES:"分类",TAGS:"标签",UNTITLED:"(未命名)"},ROOT_URL:"/blog/",CONTENT_URL:"/blog/content.json"};T.INSIGHT_CONFIG=N}(window)</script><script src="/blog/js/insight.js"></script><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><script src="//cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.js"></script><script src="//cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script><script type="text/javascript">var gitalk=new Gitalk({clientID:"adf0c8bfdd8796029917",clientSecret:"a88c2e2e83e2b180f62ad927dcb347871dfe9f4b",repo:"myblog-comment",owner:"yuyong725",admin:["yuyong725"],id:md5(location.pathname),distractionFreeMode:!0});gitalk.render("comments")</script></body></html><!-- rebuild by neat -->